package com.demo8.system.service.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.handler.inter.IExcelExportServer;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.demo8.basic.constant.CacheConstants;
import com.demo8.basic.constant.Constants;
import com.demo8.basic.constant.UserConstants;
import com.demo8.basic.domain.PageInfo;
import com.demo8.basic.exception.ServiceException;
import com.demo8.basic.utils.RedisCacheUtils;
import com.demo8.basic.utils.StringUtils;
import com.demo8.system.domain.dto.SysConfigDTO;
import com.demo8.system.domain.entity.SysConfig;
import com.demo8.system.domain.vo.SysConfigVO;
import com.demo8.system.mapper.SysConfigMapper;
import com.demo8.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author mazhenjiang
 * @description 针对表【sys_config(参数配置表)】的数据库操作Service实现
 * @createDate 2025-09-19 16:02:34
 */
@Slf4j
@Service
public class SysConfigServiceImpl implements ISysConfigService , IExcelExportServer {

    @Autowired
    private MapperFacade mapperFacade;

    @Autowired
    private SysConfigMapper sysConfigMapper;
    @Autowired
    private RedisCacheUtils redisCacheUtils;
    private static final int PAGE_SIZE = 50_000;
    @Override
    public PageInfo<SysConfig> selectConfigPage(SysConfigDTO sysConfigDTO) {
        // 创建 MyBatis-Plus 分页对象
        Page<SysConfig> page = new Page<>(sysConfigDTO.getPageNum(), sysConfigDTO.getPageSize());
        QueryWrapper<SysConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(sysConfigDTO.getConfigName()), "config_name", sysConfigDTO.getConfigName());
        // 执行分页查询（使用 MyBatis-Plus 的 wrapper 如果需要条件）
        Page<SysConfig> resultPage = sysConfigMapper.selectPage(page, queryWrapper);

        // 转换为 PageInfo
        return PageInfo.of(resultPage);
    }

    @Override
    public PageInfo<SysConfigVO> selectConfigVOPage(SysConfigDTO sysConfigDTO) {
        Page<SysConfig> page = new Page<>(sysConfigDTO.getPageNum(), sysConfigDTO.getPageSize());
        QueryWrapper<SysConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(sysConfigDTO.getConfigName()), "config_name", sysConfigDTO.getConfigName());
        // 执行分页查询（使用 MyBatis-Plus 的 wrapper 如果需要条件）
        Page<SysConfig> resultPage = sysConfigMapper.selectPage(page, queryWrapper);

        // 如果需要转换 VO 对象
        List<SysConfigVO> voList = resultPage.getRecords().stream()
                .map(config -> mapperFacade.map(config, SysConfigVO.class))
                .collect(Collectors.toList());
        // 使用自定义转换方法
        return PageInfo.of(resultPage, voList);
    }

    @Override
    public PageInfo<SysConfigVO> selectConfigVOCustomConditionPage(SysConfigDTO sysConfigDTO) {
        Page<SysConfigVO> page = new Page<>(sysConfigDTO.getPageNum(), sysConfigDTO.getPageSize());
        IPage<SysConfigVO> resultPage = sysConfigMapper.selectByCustomCondition(page, sysConfigDTO);
        return PageInfo.of(resultPage);
    }

    @Override
    public List<SysConfig> selectConfigList(SysConfigDTO sysConfigDTO) {
        return sysConfigMapper.selectByMap(new HashMap<>());
    }

    @Cacheable(cacheNames = "sysConfig")
    @Override
    public SysConfigVO selectOneById(Integer configId) {
        log.info(">>>>>>>>>>>>>>>>selectOneById:{}", configId);
        return mapperFacade.map(this.sysConfigMapper.selectById(configId), SysConfigVO.class);
    }

    @Override
    public List<SysConfigVO> selectListByParam(SysConfigDTO sysConfigDTO) {

        QueryWrapper<SysConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(sysConfigDTO.getConfigName()), "config_name", sysConfigDTO.getConfigName());
        queryWrapper.like(StringUtils.isNotBlank(sysConfigDTO.getConfigKey()), "config_key", sysConfigDTO.getConfigKey());
        List<SysConfig> sysConfigs = sysConfigMapper.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(sysConfigs)) {
            return mapperFacade.mapAsList(sysConfigs, SysConfigVO.class);
        }
        return null;
    }

    @Override
    public Boolean selectCaptchaEnabled() {
        return Convert.toBool(selectConfigValueByKey("sys.account.captchaEnabled"), true);
    }

    @Override
    public String selectConfigValueByKey(String configKey) {
        //先从缓存获取，缓存没有时从数据库获取
        String cacheValue = redisCacheUtils.getCacheObject(getCacheConfigKey(configKey));
        if (StringUtils.isNotEmpty(cacheValue)) {
            return cacheValue;
        }
        QueryWrapper<SysConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(configKey), "config_key", configKey);
        SysConfig sysConfig = sysConfigMapper.selectOne(queryWrapper);
        if (Objects.nonNull(sysConfig)) {
            redisCacheUtils.setCacheObject(getCacheConfigKey(configKey), sysConfig.getConfigValue());
            return sysConfig.getConfigValue();
        }

        return StringUtils.EMPTY;
    }

    @Override
    public Pair<Boolean, String> checkConfigKeyUnique(SysConfigDTO sysConfigDTO) {
        Long configId = Objects.isNull(sysConfigDTO.getConfigId()) ? -1L : sysConfigDTO.getConfigId();
        QueryWrapper<SysConfig> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(StringUtils.isNotBlank(sysConfigDTO.getConfigKey()), "config_key", sysConfigDTO.getConfigKey());
        SysConfig sysConfig = sysConfigMapper.selectOne(queryWrapper);
        if (Objects.nonNull(sysConfig) && !sysConfig.getConfigId().equals(configId)) {
            return Pair.of(UserConstants.NOT_UNIQUE, sysConfigDTO.getConfigKey() + "参数键名已存在");
        }
        return Pair.of(UserConstants.UNIQUE, null);
    }

    @Override
    public int insertConfig(SysConfigDTO sysConfigDTO) {

        int insert = this.sysConfigMapper.insert(mapperFacade.map(sysConfigDTO, SysConfig.class));
        if (insert > 0) {
            redisCacheUtils.setCacheObject(getCacheConfigKey(sysConfigDTO.getConfigKey()), sysConfigDTO.getConfigValue());
        }
        return insert;
    }

    public int updateConfig(SysConfigDTO sysConfigDTO) {

        //删除缓存
        SysConfig sourceSysConfig = this.sysConfigMapper.selectById(sysConfigDTO.getConfigId());
        if (!StringUtils.equals(sourceSysConfig.getConfigKey(), sysConfigDTO.getConfigKey())) {
            redisCacheUtils.deleteObject(getCacheConfigKey(sourceSysConfig.getConfigKey()));
        }

        int updateById = this.sysConfigMapper.updateById(mapperFacade.map(sysConfigDTO, SysConfig.class));
        if (updateById > 0) {
            redisCacheUtils.setCacheObject(getCacheConfigKey(sysConfigDTO.getConfigKey()), sysConfigDTO.getConfigValue());
        }
        return updateById;
    }

    @Override
    public void deleteConfigByIds(Long[] ids) {

        for (Long id : ids) {
            SysConfig sysConfig = this.sysConfigMapper.selectById(id);
            if (StringUtils.equals(UserConstants.YES, sysConfig.getConfigType())) {
                throw new ServiceException(StringUtils.format("内置参数【%1$s】不允许删除", sysConfig.getConfigKey()));
            }
            this.sysConfigMapper.deleteById(id);
            this.redisCacheUtils.deleteObject(getCacheConfigKey(sysConfig.getConfigKey()));
        }
    }

    @Override
    public void clearConfigCache() {
        Collection<String> keys = redisCacheUtils.keys(CacheConstants.SYS_CONFIG_KEY + "*");
        redisCacheUtils.deleteObject(keys);
    }

    @Override
    public void reloadConfigCache() {
        List<SysConfig> sysConfigs = this.sysConfigMapper.selectList(new QueryWrapper<>());
        for (SysConfig sysConfig : sysConfigs) {
            redisCacheUtils.setCacheObject(getCacheConfigKey(sysConfig.getConfigKey()), sysConfig.getConfigValue());
        }
    }

    @Override
    public void refreshConfigCache() {
        clearConfigCache();
        reloadConfigCache();
    }

    /**
     * 导出三步走
     * 1. 查询符合条的数据
     * 2. 拼接Excel
     * 3. 设置响应头
     * 4. 响应数据
     *
     * @param sysConfigDTO
     * @return
     */
    @Override
    public void export(SysConfigDTO sysConfigDTO, HttpServletResponse response) {
        QueryWrapper<SysConfig> sysConfigQueryWrapper = new QueryWrapper<>();
        sysConfigQueryWrapper.like(StringUtils.isNotBlank(sysConfigDTO.getConfigName()), "config_name", sysConfigDTO.getConfigName());
        sysConfigQueryWrapper.like(StringUtils.isNotBlank(sysConfigDTO.getConfigKey()), "config_key", sysConfigDTO.getConfigKey());
        sysConfigQueryWrapper.eq(StringUtils.isNotBlank(sysConfigDTO.getConfigType()), "config_type", sysConfigDTO.getConfigType());
        sysConfigQueryWrapper.ge(Objects.nonNull(sysConfigDTO.getBeginTime()), "create_time", sysConfigDTO.getBeginTime());
        sysConfigQueryWrapper.le(Objects.nonNull(sysConfigDTO.getEndTime()), "create_time", sysConfigDTO.getEndTime());
        List<SysConfig> sysConfigs = this.sysConfigMapper.selectList(sysConfigQueryWrapper);

        ServletOutputStream out = null;
        // 2. 创建 Writer（true = 自动写表头）
        ExcelWriter writer = null;
        try {
            writer = ExcelUtil.getWriter(true);
            // 3. 中文表头映射（key=属性名，value=中文列名）
            writer.addHeaderAlias("configId", "参数主键");
            writer.addHeaderAlias("configName", "参数名称");
            writer.addHeaderAlias("configKey", "参数键名");
            writer.addHeaderAlias("configValue", "参数键值");
            writer.addHeaderAlias("configType", "系统内置");
            writer.addHeaderAlias("remark", "备注");
            writer.addHeaderAlias("createTime", "创建时间");
            writer.addHeaderAlias("updateTime", "修改时间");
            writer.write(sysConfigs, true);


            // 6. 响应流下载
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("用户列表.xlsx", "UTF-8"));
            writer.flush(response.getOutputStream(), true);
            writer.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {

            IoUtil.close(out);
        }
    }

    @Override
    public List<Object> selectListForExcelExport(Object queryParams, int pageNo) {
        Page<SysConfig> page = new Page<>(pageNo, PAGE_SIZE);
        List<SysConfig> records = sysConfigMapper.selectPage(page, null).getRecords();
        if (records.isEmpty()) {
            return null;   // 结束标志
        }
        // 转 VO（BeanUtil 复制即可）
        return records.stream()
                .map(u -> {
                    SysConfigVO vo = new SysConfigVO();
                    BeanUtil.copyProperties(u, vo);
                    return vo;
                })
                .collect(Collectors.toList());
    }

    @Override
    public void exportBigData(SysConfigDTO sysConfigDTO, HttpServletResponse response) {
        try {
            String fileName = URLEncoder.encode("参数配置千万级导出.xlsx", "UTF-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);


            ExportParams params = new ExportParams("参数列表", "参数配置详情", ExcelType.XSSF);
            params.setMaxNum(1_00_000);   // 100w 拆 sheet

            Workbook workbook = ExcelExportUtil.exportBigExcel(params,
                    SysConfigVO.class,
                    this,          // 查询条件
                    null);
            workbook.write(response.getOutputStream());
//            ExcelExportUtil.closeExportBigExcel();    // 删临时文件
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取缓存key
     *
     * @param configKey
     */
    private String getCacheConfigKey(String configKey) {
        return CacheConstants.SYS_CONFIG_KEY + configKey;
    }
}




